home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
umich
/
network
/
ka9q
/
ka9q_src.arc
/
LAPB.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-07-28
|
11KB
|
447 lines
/* Link Access Procedures Balanced (LAPB) - with changes for rational
* behavior over packet radio
*/
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ax25.h"
#include "lapb.h"
int conok = 1; /* DG2KK */
extern struct tcb;
#define NULLTCB (struct tcb *)0 /* DG2KK */
/* Process incoming frames */
lapb_input(axp,cmdrsp,bp)
struct ax25_cb *axp; /* Link control structure */
char cmdrsp; /* Command/response flag */
struct mbuf *bp; /* Rest of frame, starting with ctl */
{
int16 ftype();
char control;
char frej = 0; /* FRMR reject reason code */
char class; /* General class (I/S/U) of frame */
int16 type; /* Specific type (I/RR/RNR/etc) of frame */
char pf; /* extracted poll/final bit */
char polled = 0;
char remote[10]; /* DG2KK */
if(bp == NULLBUF || axp == NULLAX25){
free_p(bp);
return;
}
control = pullchar(&bp);
type = ftype(control);
class = type & 0x3;
pf = control & PF;
/* Check for polls */
if(cmdrsp == COMMAND && pf)
polled = YES;
/* While we're disconnected, ignore all except SABM and DISC */
if(axp->state == DISCONNECTED && type != SABM && type != DISC){
if(polled)
axp->response = DM;
goto done;
}
/* Process implicit acknowledgements in all but U-frames */
if(class != U && ackours(axp,(control >> 5) & MMASK) == -1)
frej = Z; /* Out of range sequence number */
if(type != I && type != FRMR && len_mbuf(bp) != 0)
frej |= X|W; /* I-field not allowed */
/* Process final bit if a poll was outstanding */
if(axp->waitack && class != U && pf && cmdrsp == RESPONSE){
axp->waitack = NO;
axp->retries = 0;
stop_timer(&axp->t1);
/* Pick up retransmission at proper point */
axp->vs -= axp->unack;
axp->vs &= MMASK;
axp->unack = 0;
}
/* Resend FRMR for all except certain U while in error state */
if(axp->state == FRAMEREJECT && class != U){
goto done;
}
switch(type){
case SABM: /* Initialize or reset link */
/* we arrive here when we receive a SABM directed to us. */
/* put remote callsign in 'remote' */
pax25(remote, &axp->addr.dest); /* DG2KK */
log(NULLTCB,"Connect request from %s (%d)", remote, conok);
if (conok != 0 || axp->state != DISCONNECTED) { /* DG2KK */
switch(axp->state){
case DISCONNECTED:
case FRAMEREJECT:
case CONNECTED: /* note fall-through */
lapbstate(axp,CONNECTED);/* Resets state counters */
axp->response = UA;
break;
case DISCPENDING:
axp->response = DM;
break;
case SETUP:
axp->response = UA;
break;
}
} else {
polled = NO; /* DG2KK */
}
break;
case UA:
axp->retries = 0;
switch(axp->state){
case CONNECTED:
lapbstate(axp,SETUP);
sendctl(axp,COMMAND,SABM|PF);
break;
case SETUP:
stop_timer(&axp->t1);
lapbstate(axp,CONNECTED);
break;
case DISCPENDING:
lapbstate(axp,DISCONNECTED);
break;
/* note - ignored if DISCONNECTED or in FRAMEREJECT state */
}
break;
case DISC:
switch(axp->state){
case SETUP:
lapbstate(axp,DISCONNECTED);
axp->response = DM;
break;
case DISCPENDING:
axp->response = UA;
break;
default:
axp->response = UA;
lapbstate(axp,DISCONNECTED);
break;
}
break;
case RR:
axp->remotebusy = NO;
break;
case RNR:
axp->remotebusy = YES;
axp->retries = 0;
start_timer(&axp->t1); /* Probe as long as necessary */
break;
case REJ:
/* Crank back V(s) to start of queue */
axp->vs -= axp->unack;
axp->vs &= MMASK;
axp->unack = 0;
break;
case I:
if(len_mbuf(axp->rxq) >= axp->window){
/* Too bad he didn't listen to us; he'll
* have to resend the frame later. This
* drastic action is necessary to avoid
* deadlock.
*/
axp->response = RNR;
free_p(bp);
bp = NULLBUF;
break;
}
/* Reject or ignore I-frames with receive sequence number errors */
if(((control>>1) & MMASK) != axp->vr){
if(axp->proto == V1 || !axp->rejsent){
axp->rejsent = YES;
axp->response = REJ;
}
break;
}
axp->rejsent = NO;
axp->vr = (axp->vr+1) & MMASK;
axp->response = len_mbuf(axp->rxq) >= axp->window ? RNR : RR;
procdata(axp,bp);
bp = NULLBUF;
break;
case FRMR:
if(axp->state == FRAMEREJECT || axp->state == CONNECTED){
lapbstate(axp,SETUP);
sendctl(axp,COMMAND,SABM|PF);
}
break;
case DM:
switch(axp->state){
case SETUP:
case DISCPENDING:
lapbstate(axp,DISCONNECTED);
break;
default:
lapbstate(axp,SETUP);
sendctl(axp,COMMAND,SABM|PF);
}
break;
default:
frej |= W;
break;
}
done:
free_p(bp); /* In case anything's left */
/* Check if we have to make some sort of response to this frame */
if(frej){
/* Frame reject state, respond only with FRMR */
frmr(axp,control,frej);
return;
}
/* If we're being polled or have a U-frame response, respond
* immediately
*/
if(polled || (axp->response & 0x3) == U){
if(axp->response == 0)
axp->response = len_mbuf(axp->rxq) >= axp->window ? RNR : RR;
sendctl(axp,RESPONSE,axp->response|PF);
stop_timer(&axp->t2); /* No need for delayed response */
axp->response = 0;
}
/* See if we can send some data, perhaps piggybacking an ack.
* If successful, lapb_output will clear axp->response.
*/
lapb_output(axp);
if(axp->response){
/* We STILL owe him an ack. Try to delay it, but if T2 is
* disabled, send it right away
*/
if(axp->t2.start != 0){
start_timer(&axp->t2);
} else {
sendctl(axp,RESPONSE,axp->response);
axp->response = 0;
}
}
/* Restart timer T1 if there are still unacknowledged I-frames or
* a poll outstanding.
*/
if(axp->state != DISCONNECTED && (axp->unack !=0 || axp->waitack))
start_timer(&axp->t1);
/* Start timer T3 (the idle poll timer) under AX.25 V2.0
* whenever T1 is stopped, unless we are disconnected
*/
if(axp->state != DISCONNECTED && axp->t1.state != TIMER_RUN
&& axp->t3.start != 0 && axp->proto == V2)
start_timer(&axp->t3);
else
stop_timer(&axp->t3);
/* Empty the trash */
if(axp->state == DISCONNECTED)
del_ax25(axp);
}
/* Handle incoming acknowledgements for frames we've sent.
* Free frames being acknowledged.
* Return -1 to cause a frame reject if number is bad, 0 otherwise
*/
static
ackours(axp,n)
struct ax25_cb *axp;
char n;
{
struct mbuf *bp;
int acked = 0; /* Count of frames acked by this ACK */
int oldest; /* Seq number of oldest unacked I-frame */
/* Free up acknowledged frames by purging frames from the I-frame
* transmit queue. Start at the remote end's last reported V(r)
* and keep going until we reach the new sequence number.
* If we try to free a null pointer,
* then we have a frame reject condition.
* Stop the T1 timer if at least one frame is being acknowledged;
* it will be restarted again if not all frames were acknowledged.
*/
oldest = (axp->vs - axp->unack) & MMASK;
while(axp->unack != 0 && oldest != n){
if((bp = dequeue(&axp->txq)) == NULLBUF){
/* Acking unsent frame */
return -1;
}
free_p(bp);
axp->unack--;
acked++;
stop_timer(&axp->t1);
axp->retries = 0;
oldest = (oldest + 1) & MMASK;
}
/* If user has set a transmit upcall, indicate how many frames
* may be queued
*/
if(acked != 0 && axp->t_upcall != NULLVFP)
(*axp->t_upcall)(axp,axp->paclen * (axp->maxframe - axp->unack));
return 0;
}
/* Generate Frame Reject (FRMR) response
* If reason != 0, this is the initial error frame
* If reason == 0, resend the last error frame
*/
frmr(axp,control,reason)
register struct ax25_cb *axp;
char control;
char reason;
{
struct mbuf *frmrinfo;
register char *cp;
if(reason != 0){
cp = axp->frmrinfo;
*cp++ = control;
*cp++ = axp->vr << 5 || axp->vs << 1;
*cp = reason;
}
lapbstate(axp,FRAMEREJECT);
if((frmrinfo = alloc_mbuf(3)) == NULLBUF)
return; /* No memory */
frmrinfo->cnt = 3;
memcpy(frmrinfo->data,axp->frmrinfo,3);
sendframe(axp,RESPONSE,FRMR|(control&PF),frmrinfo);
}
/* Send S or U frame to currently connected station */
sendctl(axp,cmdrsp,cmd)
struct ax25_cb *axp;
char cmdrsp,cmd;
{
int16 ftype();
if((ftype(cmd) & 0x3) == S) /* Insert V(R) if S frame */
cmd |= (axp->vr << 5);
sendframe(axp,cmdrsp,cmd,NULLBUF);
}
/* Start data transmission on link, if possible
* Return number of frames sent
*/
int
l